/**
 * 
 */
package epsofts.Neutron;

import static org.junit.Assert.*;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * @author Pierre ESCALLIER
 *
 */
public class testUnitPlateau
{

	/**
	 * T�moin chaine d'un plateau initial.
	 */
	private static final String sm_plateau_initial_string = "rrrrr.......n.......bbbbb B";
	private static final Plateau sm_plateau_initial = new Plateau();
	private Plateau m_plateau = null;
	
	/**
	 * assertChainePlateau
	 * v�rifie que le plateau corresponde � la chaine donn�e.
	 */
	private static void assertChainePlateau(final Plateau plateau, final String chaine)
	{
		assertTrue(String.format("attendu; '%s', actuel '%s'.", chaine, plateau.toString()), plateau.toString().compareTo(chaine) == 0);
	}
	
	/**
	 * @throws java.lang.Exception
	 */
	@Before
	public void setUp() throws Exception
	{
		m_plateau = new Plateau();
	}

	/**
	 * @throws java.lang.Exception
	 */
	@After
	public void tearDown() throws Exception
	{
		m_plateau = null;
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#Plateau(epsofts.Neutron.Plateau)}.
	 */
	@Test
	public final void testPlateauPlateau()
	{
		Plateau plateau_copie = m_plateau.clone();
		
		// La copie est affect�e
		assertNotNull("Plateau NULL", plateau_copie);
		
		// Les r�f�rences pointent deux objets distincts
		assertNotSame("R�f�rence identique", m_plateau, plateau_copie);
		
		// Les deux plateaux sont identiques
		assertEquals("Plateaux diff�rents", m_plateau, plateau_copie);
		
		// Si l'on joue un coup sur l'un, alors il n'est plus identique � l'autre
		
		Plateau coupJoue = plateau_copie.jouerCoup(new Coup((short) 20, (short) 5, (short) 12, (short) 12));
		
		// Jouer un coup ne modifie pas le plateau de départ.
		assertEquals("Plateaux différents", m_plateau, plateau_copie);
		
		// Le plateau où l'on a joué le coup est bien différent du plateau de départ.
		assertFalse("Plateaux identiques", m_plateau.equals(coupJoue));
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#Plateau(Neutron.eCase[], Neutron.eCase, short)}.
	 */
	@Test
	public final void testPlateauECaseArrayECaseShort()
	{
		eCase[] cases = new eCase[25];
		for(int i=0; i<25; ++i)
		{
			cases[i] = eCase.VIDE;
			if ((i>4) && (i<10)) cases[i] = eCase.BLEUE;
			if ((i>14) && (i<20)) cases[i] = eCase.ROUGE;
		}
		cases[10] = eCase.NEUTRON;
		
		String chaine_temoin = ".....bbbbbn....rrrrr..... B";
		
		Plateau plateau = new Plateau(cases, eCase.BLEUE, (short) 10, null);
		
		// La r�f�rence est assign�e
		assertNotNull("Plateau NULL", plateau);
		// Le plateau est bien identique � la valeur attendue
		assertChainePlateau(plateau, chaine_temoin);
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#Plateau()}.
	 */
	@Test
	public final void testPlateau()
	{
		Plateau plateau = new Plateau();
		
		// La r�f�rence est assign�e
		assertNotNull("Plateau NULL", plateau);
		// Le plateau est bien identique � la valeur attendue
		assertChainePlateau(plateau, sm_plateau_initial_string);
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#jouerCoup(epsofts.Neutron.Coup)}.
	 */
	@Test
	public final void testJouerCoup()
	{
		Plateau plateau = new Plateau();
		
		Coup coup = new Coup((short) 21, (short) 6, (short) 12, (short) 12);
		String chaine_plateau_coup1 = "rrrrr.b.....n.......b.bbb R";
		
		plateau = plateau.jouerCoup(coup);
		
		// Le plateau (initial) sur lequel on a jou� le coup1 est bien identique � la valeur attendue
		assertChainePlateau(plateau, chaine_plateau_coup1);
		
		plateau = plateau.jouerCoup(new Coup((short) 2, (short) 17, (short) 12, (short) 18));
		String chaine_plateau_coup2 = "rr.rr.b..........rn.b.bbb B";
		
		// Le plateau (initial + coup 1) sur lequel on a jou� le coup2 est bien identique � la valeur attendue
		assertChainePlateau(plateau, chaine_plateau_coup2);
		
		Coup invalide = new Coup((short) 2, (short) 6, (short) 12, (short) 17);
		plateau = plateau.jouerCoup(invalide);
		System.out.println("Attention, on peut jouer des coups invalides.");
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#testerValiditeCoup(epsofts.Neutron.Coup)}.
	 */
	@Test
	public final void testTesterValiditeCoup()
	{
		// On bouge une bille correctement, pas le neutron
		Coup coup1ok = new Coup((short) 21, (short) 6, (short) 12, (short) 12);
		// On bouge une bille correctement, et le neutron
		Coup coup1nok=new Coup((short) 21, (short) 9, (short) 12, (short) 14);
		// On bouge le neutron correctement, et une bille correctement
		Coup coup2ok = new Coup((short) 2, (short) 17, (short) 12, (short) 18);
		// On ne bouge pas le neutron, on bouge une bille correctement
		Coup coup2nok1=new Coup((short) 0, (short) 15, (short) 12, (short) 12);
		// On bouge le neutron sur une bille, on bouge une bille corretement
		Coup coup2nok2=new Coup((short) 4, (short) 19, (short) 12, (short) 6);
		// On bouge le neutron pas assez loin, on bouge une bille correctement
		Coup coup2nok3=new Coup((short) 3, (short) 9, (short) 12, (short) 13);
		// On bouge le neutron correctement, on bouge une bille incorrectement
		Coup coup2nok4=new Coup((short) 4, (short) 5, (short) 12, (short) 14);
		// On bouge le neutron correctement, on bouge une bille sur une bille
		Coup coup2nok5=new Coup((short) 2, (short) 22, (short) 12, (short) 10);
		// On bouge le neutron correctement, on bouge une bille sur le neutron
		Coup coup2nok6=new Coup((short) 2, (short) 15, (short) 12, (short) 14);
		// On bouge le neutron correctement, on joue une case vide pour bille
		Coup coup2nok7=new Coup((short) 7, (short) 9, (short) 12, (short) 16);
		// On bouge une case vide pour neutron, on joue une bille correctement
		Coup coup2nok8=new Coup((short) 0, (short) 15, (short) 13, (short) 7);
		// On bouge le neutron correctement, on joue une bille de mauvaise couleur
		Coup coup2nok9=new Coup((short) 20, (short) 5, (short) 12, (short) 8);
		
		// On ne peut pas boucher le neutron lors du coup initial
		assertFalse("On a boug� le neutron", sm_plateau_initial.testerValiditeCoup(coup1nok));
		// Le coup correct ne pose pas de probl�mes
		assertTrue("Le coup �tait correct", sm_plateau_initial.testerValiditeCoup(coup1ok));
		
		Plateau plateau = new Plateau();
		plateau = plateau.jouerCoup(coup1ok);
		// Le coup correct est bien jou�
		assertChainePlateau(plateau, "rrrrr.b.....n.......b.bbb R");
		
		// On doit bouger le neutron pass� le premier coup
		assertFalse("On n'avait pourtant pas boug� le neutron", plateau.testerValiditeCoup(coup2nok1));
		
		// On ne peut pas bouger le neutron sur une case non vide
		assertFalse("On a boug� le neutron sur une bille", plateau.testerValiditeCoup(coup2nok2));
		
		// On doit bouger au maximum de la direction
		assertFalse("On a pas boug� au maximum de la direction", plateau.testerValiditeCoup(coup2nok3));
		
		// On doit bouger la bille correctement
		assertFalse("On a boug� la bille incorrectement", plateau.testerValiditeCoup(coup2nok4));
		
		// On doit bouger la bille sur une case libre
		assertFalse("On a boug� la bille sur une bille", plateau.testerValiditeCoup(coup2nok5));
		
		// On ne peut pas bouger la bille sur le neutron
		assertFalse("On a boug� la bille sur le neutron", plateau.testerValiditeCoup(coup2nok6));
		
		// On doit bouger une bille
		assertFalse("On a boug� une case vide au lieu d'une bille", plateau.testerValiditeCoup(coup2nok7));
		
		// On doit bouger le neutron
		assertFalse("On a boug� une case vide au lieu du neutron", plateau.testerValiditeCoup(coup2nok8));
		
		// On doit bouger une bille de la bonne couleur
		assertFalse("On a boug� une bille de mauvaise couleur", plateau.testerValiditeCoup(coup2nok9));
		
		// On peut jouer le coup 2
		assertTrue("On a pas pu jouer le coup pourtant valide", plateau.testerValiditeCoup(coup2ok));
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#testerMouvementBille(short, short)}.
	 */
	@Test
	public final void testTesterMouvementBilleShortShort()
	{
		// On teste que tous les r�sultats soient correct pour le plateau initial et la bille 22.
		for(int i=0; i < 25; ++i)
		{
			if ((i != 10) && (i != 17) && (i != 14))
			{
				assertFalse(String.format("On a pu bouger la bille 22 en %d", i), sm_plateau_initial.testerMouvementBille((short) 22, (short) i));
			}
			else
			{
				assertTrue(String.format("On a pas pu bouger la bille 22 en %d", i), sm_plateau_initial.testerMouvementBille((short) 22, (short) i));
			}
		}
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#testerMouvementBille(short, short, short)}.
	 */
	@Test
	public final void testTesterMouvementBilleShortShortShort()
	{
		// On teste que tous les r�sultats soient corrects pour le plateau initial, bille 20, avec le neutron suppos� en 10
		for(int i=0; i<25; ++i)
		{
			if ((i != 15) && (i != 8))
			{
				assertFalse(String.format("On a pu bouger la bille 20 en %d (neutron en 10)", i), sm_plateau_initial.testerMouvementBille((short) 20, (short) i, (short) 10));
			}
			else
			{
				assertTrue(String.format("On a pas pu bouger la bille 20 en %d (neutron en 10)", i), sm_plateau_initial.testerMouvementBille((short) 20, (short) i, (short) 10));
			}
		}
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#listerCoupsBille(short)}.
	 */
	@Test
	public final void testListerCoupsBilleShort()
	{
		// On teste toutes les directions pour le plateau initial et la bille 22.
		short[] coups = sm_plateau_initial.listerCoupsBille((short) 22);
		for(int i=0; i<8; ++i)
		{
			switch(i)
			{
				case 0:
					assertTrue(String.format("Coup direction %d incorrect : %d", i, coups[i]), coups[i] == 17);
					break;
				case 1:
					assertTrue(String.format("Coup direction %d incorrect : %d", i, coups[i]), coups[i] == 14);
					break;
				case 7:
					assertTrue(String.format("Coup direction %d incorrect : %d", i, coups[i]), coups[i] == 10);
					break;
				default:
					assertTrue(String.format("Coup direction %d incorrect : %d", i, coups[i]), coups[i] == 22);
			}
		}
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#listerCoupsBille(short, short)}.
	 */
	@Test
	public final void testListerCoupsBilleShortShort()
	{
		// On teste toutes les directions pour le plateau initial, bille 20 et neutron suppos� en 8.
		short[] coups = sm_plateau_initial.listerCoupsBille((short) 20, (short) 8);
		for(int i=0; i<8; ++i)
		{
			switch(i)
			{
				case 0:
					assertTrue(String.format("Coup direction %d incorrect : %d", i, coups[i]), coups[i] == 5);
					break;
				case 1:
					assertTrue(String.format("Coup direction %d incorrect : %d", i, coups[i]), coups[i] == 12);
					break;
				default:
					assertTrue(String.format("Coup direction %d incorrect : %d", i, coups[i]), coups[i] == 20);
			}
		}
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#inverserPlateau()}.
	 */
	@Test
	public final void testInverserPlateau()
	{
		// Plateau non sym�trique (terminal bleu, aleatoire).
		eCase[] cases =	{	eCase.VIDE,			eCase.VIDE, 		eCase.VIDE, 		eCase.ROUGE, 	eCase.ROUGE,
									eCase.VIDE,			eCase.BLEUE, 	eCase.VIDE, 		eCase.VIDE, 		eCase.VIDE,
									eCase.BLEUE,			eCase.BLEUE, 	eCase.VIDE, 		eCase.VIDE, 		eCase.VIDE,
									eCase.NEUTRON, 	eCase.ROUGE,	eCase.ROUGE, 	eCase.VIDE, 		eCase.ROUGE,
									eCase.VIDE, 			eCase.VIDE, 		eCase.BLEUE, 	eCase.VIDE, 		eCase.BLEUE	};
		Plateau plateau = new Plateau(cases, eCase.ROUGE, (short) 10, null);
		
		// On v�rifie le plateau entr� ci-dessus
		assertChainePlateau(plateau, "...rr.b...bb...nrr.r..b.b R");
		
		plateau = plateau.inverserPlateau();
		
		// On v�rifie que l'inverse soit �gal � la valeur attendue
		assertChainePlateau(plateau, "r.r..b.bbn...rr...r.bb... B");
		
		plateau = plateau.inverserPlateau();
		
		// On v�rifie que l'inverse de l'inverse soit �gal au plateau de d�part
		assertChainePlateau(plateau, "...rr.b...bb...nrr.r..b.b R");		
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#toString()}.
	 */
	@Test
	public final void testToString()
	{
		// On v�rifie pour le plateau initial
		assertChainePlateau(sm_plateau_initial, "rrrrr.......n.......bbbbb B");
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#listerCoups()}.
	 */
	@Test
	public final void testListerCoups()
	{
		// On charge un plateau n'autorisant que 5 coups pr�cis au joueur Rouge, on va ainsi pouvoir tous les tester.
		eCase cases[] = new eCase[25];
		for(int i=0; i<25; ++i)
		{
			// On met 4 rouges sur le cot� droit.
			if ((i % 5) == 4)		cases[i] = eCase.ROUGE;
			// On met toutes les bleues sur leur ligne de d�part.
			if (i < 5)				cases[i] = eCase.BLEUE;
		}
		// On met une rouge en 20
		cases[20] = eCase.ROUGE;
		// On met le neuton � son point de d�part
		cases[12] = eCase.NEUTRON;
		
		// On sais que tous les coups possibles pour rouge sont les suivants :
		Set<Coup> coups_possibles = new HashSet<Coup>();
		coups_possibles.add(new Coup((short) 14, (short) 22, (short) 12, (short) 7));
		coups_possibles.add(new Coup((short) 19, (short) 23, (short) 12, (short) 8));
		coups_possibles.add(new Coup((short) 14, (short) 22, (short) 12, (short) 10));
		coups_possibles.add(new Coup((short) 9, (short) 21, (short) 12, (short) 6));
		
		Plateau plateau = new Plateau(cases, eCase.ROUGE, (short) 10, null);
		Set<Coup> liste_coups = plateau.listerCoups();
		
		Set<Coup> coups_en_trop = new HashSet<Coup>(liste_coups);
		coups_en_trop.removeAll((Collection<Coup>) coups_possibles);
		
		Set<Coup> coups_manquants = new HashSet<Coup>(coups_possibles);
		coups_manquants.removeAll((Set<Coup>) liste_coups);
		
		assertTrue(String.format("Il y a %d coups en trop.", coups_en_trop.size()), coups_en_trop.isEmpty());
		assertTrue(String.format("Il manque %d coups", coups_manquants.size()), coups_manquants.isEmpty());

	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#testerPlateauTerminal()}.
	 */
	@Test
	public final void testTesterPlateauTerminalBoolean()
	{
		eCase[] cases =	{	eCase.ROUGE,		eCase.VIDE, 		eCase.VIDE, 		eCase.ROUGE, 	eCase.ROUGE,
									eCase.VIDE,			eCase.BLEUE, 	eCase.BLEUE, 	eCase.VIDE, 		eCase.VIDE,
									eCase.BLEUE,			eCase.VIDE,	 	eCase.BLEUE,		eCase.ROUGE,	eCase.VIDE,
									eCase.NEUTRON, 	eCase.VIDE,		eCase.ROUGE, 	eCase.VIDE, 		eCase.VIDE,
									eCase.BLEUE, 		eCase.VIDE, 		eCase.VIDE, 		eCase.VIDE, 		eCase.VIDE };
		Plateau plateau = new Plateau(cases, eCase.ROUGE, (short) 10, null);
		
		eCase gagnant = plateau.testerPlateauTerminal(true);
		
		assertTrue(String.format("Le plateau devrait �tre bleu gagnant '%s' (%s)", plateau, gagnant), gagnant == eCase.BLEUE);
		
		// Si on inverse la couleur des billes
		for(int i=0; i < 25; ++i)
		{
			cases[i] = Plateau.inverserBille(cases[i]);
		}
		
		plateau = new Plateau(cases, eCase.ROUGE, (short) 10, null);
		
		gagnant = plateau.testerPlateauTerminal(true);
		assertTrue(String.format("Le plateau ne devrait pas �tre terminal '%s' (%s)", plateau, gagnant), gagnant == eCase.VIDE);
		
		// Cas de bug ?
		
		eCase[] ncases =	{	eCase.ROUGE,		eCase.VIDE, 		eCase.ROUGE, 	eCase.ROUGE, 	eCase.ROUGE,
										eCase.VIDE,			eCase.VIDE, 		eCase.VIDE, 		eCase.VIDE, 		eCase.VIDE,
										eCase.VIDE,			eCase.VIDE,	 	eCase.VIDE,		eCase.VIDE,		eCase.BLEUE,
										eCase.NEUTRON, 	eCase.VIDE,		eCase.VIDE, 		eCase.VIDE, 		eCase.BLEUE,
										eCase.BLEUE, 		eCase.VIDE, 		eCase.BLEUE, 	eCase.ROUGE, 	eCase.BLEUE};
		plateau = new Plateau(ncases, eCase.ROUGE, (short) 10, null);
		
		gagnant = plateau.testerPlateauTerminal(true);
		assertTrue(String.format("Le plateau ne devrait pas �tre terminal '%s' (%s)", plateau, gagnant), gagnant == eCase.VIDE);
		
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#getTour()}.
	 */
	//@Test
	public final void testGetTour()
	{
		warn("testGetTour Not yet implemented"); // TODO
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#getCases()}.
	 */
	//@Test
	public final void testGetCases()
	{
		warn("testGetCases Not yet implemented"); // TODO
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#getNeutron()}.
	 */
	//@Test
	public final void testGetNeutron()
	{
		warn("testGetNeutron Not yet implemented"); // TODO
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#estUnPlateauInitial()}.
	 */
	//@Test
	public final void testEstUnPlateauInitial()
	{
		warn("testEstUnPlateauInitial Not yet implemented"); // TODO
	}

	/**
	 * Test method for {@link epsofts.Neutron.Plateau#getGeneration()}.
	 */
	//@Test
	public final void testGetGeneration()
	{
		warn("testGetGeneration Not yet implemented"); // TODO
	}

	/**
	 * Test method for {@link java.lang.Object#clone()}.
	 */
	//@Test
	public final void testClone()
	{
		warn("testClone Not yet implemented"); // TODO
	}

	private static final void warn(String msg)
	{
		System.err.println("Warning: "+msg);
	}
}
